package com.dapeng.cleardatabackend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dapeng.cleardatabackend.common.ErrorCode;
import com.dapeng.cleardatabackend.common.LoginUserHolder;
import com.dapeng.cleardatabackend.exception.BusinessException;
import com.dapeng.cleardatabackend.exception.ThrowUtils;
import com.dapeng.cleardatabackend.mapper.UserMapper;
import com.dapeng.cleardatabackend.model.dto.user.*;
import com.dapeng.cleardatabackend.model.entity.User;
import com.dapeng.cleardatabackend.model.enums.UserRoleEnum;
import com.dapeng.cleardatabackend.model.enums.UserStatusEnum;
import com.dapeng.cleardatabackend.model.vo.UserVO;
import com.dapeng.cleardatabackend.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.session.Session;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.dapeng.cleardatabackend.contant.RedisConstant.REGISTER_LOCK;
import static com.dapeng.cleardatabackend.contant.UserConstant.*;

/**
 * @author dapeng
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2023-11-09 14:26:05
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public long userRegister(UserRegisterRequest userRegisterRequest) {
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        //账户不能重复
        RLock rLock = redissonClient.getLock(String.format(REGISTER_LOCK, userAccount));
        try {
            if (rLock.tryLock(0, -1, TimeUnit.SECONDS)) {
                log.info("占用注册锁:{}", userAccount);
                boolean exists = baseMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUserAccount, userAccount));
                ThrowUtils.throwIf(exists, ErrorCode.PARAMS_ERROR, "该账号已被使用");
                // 2.加密
                String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
                // 3.插入数据
                User user = new User();
                user.setUserAccount(userAccount);
                user.setUserPassword(encryptPassword);
                user.setUsername(userAccount);
                user.setUserRole(UserRoleEnum.USER);
                save(user);
                return user.getId();
            } else {
                log.info("注册锁被占用:{}", userAccount);
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "注册失败");
            }
        } catch (Exception e) {
            log.info("注册失败:{}", e.getMessage());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, e.getMessage());
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }

    @Override
    public UserVO userLogin(UserLoginRequest userLoginRequest, HttpServletRequest request) {
        // 1. 校验
        String userAccount = userLoginRequest.getUserAccount();
        String userPassword = userLoginRequest.getUserPassword();
        // 账户不能包含特殊字符
        Matcher matcher = Pattern.compile(USER_ACCOUNT_REGEX).matcher(userAccount);
        ThrowUtils.throwIf(matcher.find(), ErrorCode.PARAMS_ERROR, "账户不能包含特殊字符");
        // 2.加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());

        // 3.查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userAccount", userAccount);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null || !user.getUserPassword().equals(encryptPassword)) {
            log.error("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号或密码错误");
        }
        ThrowUtils.throwIf(user.getUserRole() == UserRoleEnum.BAN, ErrorCode.PARAMS_ERROR, "该用户已被封禁");
        user.setUserPassword(null);
        // 4.记录用户登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        request.getSession().setAttribute(USER_ID, user.getId());
        // 5.设置过期时间
        request.getSession().setMaxInactiveInterval(SESSION_TIMEOUT);
        return UserVO.objectToVO(user);
    }

    @Override
    public Page<UserVO> searchUsers(UserSearchRequest userSearchRequest) {
        String username = userSearchRequest.getUsername();
        int current = userSearchRequest.getCurrent();
        int pageSize = userSearchRequest.getPageSize();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        // 添加用户名条件
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like(User::getUsername, username);
        }
        Page<User> userPage = new Page<>(current, pageSize, false);
        userPage = page(userPage, queryWrapper);
        long total = Long.MAX_VALUE;
        if (userPage.getRecords().size() < pageSize) {
            total = 0;
        }
        Page<UserVO> userVoPage = new Page<>(userPage.getCurrent(), userPage.getSize(), total);
        return userVoPage.setRecords(userPage.getRecords().stream().map(UserVO::objectToVO).collect(Collectors.toList()));
    }

    @Override
    public boolean userLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public boolean updatePassword(UserUpdatePasswordRequest userUpdatePasswordRequest) {
        User loginUser = LoginUserHolder.getLoginUser();
        String oldPassword = userUpdatePasswordRequest.getOldPassword();
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + oldPassword).getBytes());
        User user = getOne(new LambdaQueryWrapper<User>().select(User::getUserPassword).eq(User::getId, loginUser.getId()));
        ThrowUtils.throwIf(!user.getUserPassword().equals(encryptPassword), ErrorCode.OPERATION_ERROR, "旧密码错误");
        String userPassword = userUpdatePasswordRequest.getUserPassword();
        String newEncryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        User newUser = new User();
        newUser.setId(loginUser.getId());
        newUser.setUserPassword(newEncryptPassword);
        return this.updateById(newUser);
    }

    @Override
    public boolean updateUser(UserUpdateMyRequest userUpdateMyRequest) {
        User loginUser = LoginUserHolder.getLoginUser();
        if (userUpdateMyRequest.getUserAccount() != null) {
            User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserAccount, userUpdateMyRequest.getUserAccount()));
            if (!user.getId().equals(loginUser.getId())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "该账号已被使用");
            }
        }
        BeanUtils.copyProperties(userUpdateMyRequest, loginUser);
        return this.updateById(loginUser);
    }

    @Override
    public Boolean updateUserAccess(UserUpdateAccessRequest userUpdateAccessRequest) {
        Long id = userUpdateAccessRequest.getId();
        UserRoleEnum userRole = userUpdateAccessRequest.getUserRole();
        UserStatusEnum userStatus = userUpdateAccessRequest.getUserStatus();
        ThrowUtils.throwIf(userRole == null && userStatus == null, ErrorCode.OPERATION_ERROR);
        if (userRole != null) {
            ThrowUtils.throwIf(userRole.equals(UserRoleEnum.SUPER_ADMIN), ErrorCode.OPERATION_ERROR, "无法设置超级管理员权限");
        }
        User loginUser = LoginUserHolder.getLoginUser();
        User user = getById(id);
        ThrowUtils.throwIf(user == null, ErrorCode.OPERATION_ERROR);
        ThrowUtils.throwIf(user.getId().equals(loginUser.getId()), ErrorCode.OPERATION_ERROR, "无法修改自己权限");
        ThrowUtils.throwIf(user.getUserRole().equals(UserRoleEnum.SUPER_ADMIN), ErrorCode.OPERATION_ERROR, "无法修改超级管理员权限");
        User newUser = new User();
        newUser.setId(id);
        if (userRole != null) {
            newUser.setUserRole(userRole);
        }
        if (userStatus != null) {
            newUser.setUserStatus(userStatus);
        }
        return this.updateById(newUser);
    }

}




